
`include "common_header.verilog" 

//  *************************************************************************
//  File : dff_fifo_noh_12347
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited. 
//  Copyright (c) 2015 Morethanip
//  Muenchner Str. 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Daniel Koehler
//  info@morethanip.com
//  *************************************************************************
//  Description: DFF based FIFO fully synchronous and with zero read latency.
//
//               Output has no hold capability (noh): When rden deasserts
//               the output data changes to the next (which in case of
//               reaching empty could be arbitrary if the input is arbitrary).
//
//               All flags are unregistered (no delay).
//
//  Version    : $Id: dff_fifo_noh_12347.v,v 1.3 2017/10/10 10:02:47 gc Exp $
//  *************************************************************************
// 
//    FIFO Name Coding:
//    ----------------
// 
//     noh:      no output hold
//       1:      Full
//       2:      Almost Full
//       3       Empty
//       4:      Almost Empty
//       7:      Supports non-power of 2 depths
//  *************************************************************************

module dff_fifo_noh_12347 (

   reset,
   clk,
   sw_reset,
   wren,
   din,
   rden,
   dout,
   ovr,
   afull,
   full,   
   aempty,
   empty,
   fflevel);
   
parameter FF_WIDTH      = 4;
parameter ADDR_WIDTH    = 4;
parameter DEPTH         = 16;
parameter AF_THRESHOLD  = 2;
parameter AE_THRESHOLD  = 2;
parameter UNDERFLOW_PROTECT = 1;//  If 1 stop read pointer advance when FIFO is empty; If 0 read pointer is unprotected

input   reset;                  //  async active high
input   clk;                    //  clock
input   sw_reset;               //  sync reset
input   wren;                   //  Write enable     
input   [FF_WIDTH - 1:0] din;   //  Input data       
input   rden;                   //  Read enable     
output  [FF_WIDTH - 1:0] dout;  //  read data        
output  ovr;                    //  FIFO overflow/underflow error
output  afull;                  //  FIFO almost full
output  full;                   //  FIFO full
output  aempty;                 //  FIFO almost empty
output  empty;                  //  FIFO empty
output  [ADDR_WIDTH:0] fflevel; //  FIFO level with extra bit for full/overflow indication
          
// outwires 
                      
wire    [FF_WIDTH - 1:0] dout; 
wire    ovr;
wire    afull; 
wire    full; 
wire    aempty;
reg     empty; 
reg     [ADDR_WIDTH:0] fflevel;

parameter PTR_WIDTH = ADDR_WIDTH+1;
parameter PTR_DEPTH = DEPTH*2;

reg     [PTR_WIDTH - 1:0] wr_ptr; 
reg     [PTR_WIDTH - 1:0] rd_ptr; 

wire    [ADDR_WIDTH-1:0]  waddr;                // array write address
wire    [ADDR_WIDTH-1:0]  raddr;                // array read address
reg     [FF_WIDTH - 1:0]  adff[ DEPTH-1:0 ];    // DFF array
wire    [FF_WIDTH - 1:0]  rdata;                // current read data

// Address Pointers
// ----------------

always @(posedge clk or posedge reset)
begin:ptrs
        
        if( reset == 1'b 1 )
        begin
                wr_ptr <= {PTR_WIDTH{1'b 0}};
                rd_ptr <= {PTR_WIDTH{1'b 0}};
                empty  <= 1'b 1;
                fflevel <= {ADDR_WIDTH+1{1'b 0}};
        end
        else
        begin
                // advance write pointer 
                
                if( sw_reset == 1'b 1)
                begin
                        wr_ptr <= {PTR_WIDTH{1'b 0}};
                end
                else if( wren==1'b 1 && (fflevel < DEPTH || rden == 1'b 1))
                begin
                        if (wr_ptr == (DEPTH-1))
                        begin
                                wr_ptr <= {PTR_WIDTH{1'b 0}};
                        end
                        else
                        begin
                                wr_ptr <= wr_ptr + {{(PTR_WIDTH-1){1'b 0}}, 1'b 1};   // +1
                        end
                end
                
                // advance read pointer 

                if( sw_reset == 1'b 1 )
                begin
                        rd_ptr <= {PTR_WIDTH{1'b 0}};
                end
                else if( rden==1'b 1 && (UNDERFLOW_PROTECT==0 || (|fflevel == 1'b 1)) )        // stop increment when empty
                begin
                        if (rd_ptr == (DEPTH-1))
                        begin
                                rd_ptr <= {PTR_WIDTH{1'b 0}};
                        end
                        else
                        begin
                                rd_ptr <= rd_ptr + {{(PTR_WIDTH-1){1'b 0}}, 1'b 1};   // +1
                        end
                end
                
                // provide empty registered and track fifo fullness
                
                if( sw_reset == 1'b 1 )
                begin
                        empty <= 1'b 1;
                        fflevel <= {ADDR_WIDTH+1{1'b 0}};
                end
                else if( rden==1'b 1 && (UNDERFLOW_PROTECT==0 || (|fflevel == 1'b 1)) )
                begin
                        
                        if ((fflevel == {{ADDR_WIDTH{1'b 0}}, 1'b 1}) && (wren == 1'b 0))
                        begin
                                empty <= 1'b 1;
                                fflevel <= {ADDR_WIDTH+1{1'b 0}};
                        end

                        else if ((fflevel > {{ADDR_WIDTH{1'b 0}}, 1'b 1}) && (wren == 1'b 0))
                        begin
                                empty <= 1'b 0;
                                fflevel <= fflevel - {{ADDR_WIDTH{1'b 0}}, 1'b 1};
                        end
                        else if ((fflevel >= {{ADDR_WIDTH{1'b 0}}, 1'b 1}) && wren == 1'b 1)
                        begin
                                empty <= 1'b 0;
                                fflevel <= fflevel;
                        end
                        else if ((fflevel == {ADDR_WIDTH+1{1'b 0}}) && wren == 1'b 1)
                        begin
                                empty <= 1'b 0;
                                fflevel <= {{ADDR_WIDTH{1'b 0}}, 1'b 1};
                        end
                end
                else if( wren == 1'b 1 && fflevel < DEPTH) // Implies rden == 1'b 0 or empty
                begin
                        empty <= 1'b 0;
                        fflevel <= fflevel + {{ADDR_WIDTH{1'b 0}}, 1'b 1};
                end
        end
end

assign waddr = wr_ptr[ADDR_WIDTH-1:0];
assign raddr = rd_ptr[ADDR_WIDTH-1:0];

//  Almost Full
//  -----------------------------------------
assign afull = (fflevel > (DEPTH - AF_THRESHOLD - 1)) ? 1'b 1 : 1'b 0;

//  Full
//  -----------------------------------------
assign full = fflevel == DEPTH;        // extra bit indicates full

//  overflow error
//  -----------------------------------------
assign ovr = full & wren & ~rden;

//  Almost Empty Flag Generation
//  ----------------------------

assign aempty = (fflevel < AE_THRESHOLD) ? 1'b 1 : 1'b 0; 

//  DFF Array Write
//  ----------------------------

always @(posedge clk or posedge reset)
begin:awrr
        
        integer i;
        
        if( reset == 1'b 1 )
        begin
                for( i=0; i < DEPTH; i=i+1 )
                begin
                        adff[i] <= {FF_WIDTH{1'b 0}};
                end
        end
        else
        begin
                if( wren==1'b 1 )
                begin
                        adff[ waddr ] <= din;
                end
        end
        
end


//  DFF Array Read
//  ----------------------------

assign rdata = adff[ raddr ];

        // zero delay

assign dout = rdata;
        

// ----------
// ASSERTIONS
// ----------

`ifdef MTIPASSERTIONS
// synthesis translate_off
// synopsys translate_off

always @(posedge clk or posedge reset)
begin:assertions
        
        if( reset == 1'b 1 )
        begin
        end
        else
        begin
            if( !sw_reset )
            begin
                
                if( rden && fflevel=={ADDR_WIDTH+1{1'b 0}} )
                begin
                        $display("%t ASSERT [dff_fifo_noh_12347:%m]: WARNING: read from empty FIFO (underflow) occured",$time);
                end
                
                if( wren && !rden && fflevel==DEPTH )     // exactly at full
                begin
                        $display("%t ASSERT [dff_fifo_noh_12347:%m]: WARNING: FIFO overflow occured",$time);
                end
            
            end
            
        end
        
end

// synopsys translate_on
// synthesis translate_on
`endif


endmodule // module dff_fifo_noh_12347
